/*
 * Created on 2007-1-8
 *
 */
package bma.common.langutil.io;

import java.net.InetAddress;
import java.net.UnknownHostException;

import bma.common.langutil.core.ExceptionUtil;

/**
 * 网络掩码地址<br>
 * 格式: 192.168.0.0/16、192.168.16.0/24、192.168.16.1/32
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 */
public class InetNetwork {

	private final long address;

	private final long mask;

	private final int maskIndex;

	public InetNetwork(InetAddress inetAddress, int maskIndex) {
		byte bytes[] = inetAddress.getAddress();
		long address = 0L;
		for (int i = 0; i < bytes.length; i++) {
			address = 256L * address + (bytes[i] & 0xff);
		}

		this.address = address;
		this.maskIndex = maskIndex;
		this.mask = -1L << 32 - maskIndex;
	}

	public InetNetwork(long address, int maskIndex) {
		this.address = address;
		this.maskIndex = maskIndex;
		this.mask = -1L << 32 - maskIndex;
	}

	public static InetNetwork create(String network) {
		if (network == null) {
			return null;
		}
		int i = 0;
		int len = network.length();
		long address = 0L;
		int ch = 0;
		do {
			if (i >= len || network.charAt(i) == '/') {
				break;
			}
			int digit = 0;
			for (; i < len && (ch = network.charAt(i)) >= '0' && ch <= 57; i++) {
				digit = (10 * digit + ch) - 48;
			}

			address = 256L * address + digit;
			if (i < len) {
				if (ch == '.') {
					i++;
				} else {
					if (ch != '/') {
						// unknow char
						return null;
					}
				}
			}
		} while (true);
		int mask;
		if (i < len && network.charAt(i) == '/') {
			mask = 0;
			for (i++; i < len && (ch = network.charAt(i)) >= '0' && ch <= 57; i++) {
				mask = (10 * mask + ch) - 48;
			}

		} else {
			mask = 32;
		}
		return new InetNetwork(address, mask);
	}

	public boolean isMatch(InetAddress inetAddress) {
		byte bytes[] = inetAddress.getAddress();
		long address = 0L;
		for (int i = 0; i < bytes.length; i++) {
			address = 256L * address + (bytes[i] & 0xff);
		}

		return (this.address & this.mask) == (address & this.mask);
	}

	public boolean isMatch(byte bytes[]) {
		long address = 0L;
		for (int i = 0; i < bytes.length; i++) {
			address = 256L * address + (bytes[i] & 0xff);
		}

		return (this.address & this.mask) == (address & this.mask);
	}

	public boolean isMatch(long address) {
		return (this.address & this.mask) == (address & this.mask);
	}

	public boolean isMatch(String ip) {
		try {
			InetAddress address = InetAddress.getByName(ip);
			return isMatch(address);
		} catch (UnknownHostException e) {
			throw ExceptionUtil.throwRuntime(e);
		}
	}

	@Override
	public String toString() {
		StringBuffer cb = new StringBuffer(40);
		for (int i = 0; i < 4; i++) {
			if (i != 0) {
				cb.append('.');
			}
			cb.append(this.address >> (3 - i) * 8 & 255L);
		}

		cb.append('/');
		cb.append(this.maskIndex);
		return cb.toString();
	}

	public static boolean isMatch(Iterable<InetNetwork> iter, String ip) {
		try {
			InetAddress address = InetAddress.getByName(ip);
			for (InetNetwork net : iter) {
				if (net.isMatch(address)) {
					return true;
				}
			}
			return false;
		} catch (Exception e) {
			throw ExceptionUtil.throwRuntime(e);
		}
	}
}
